#pragma once

#include <string>
#include <vector>
#include <fstream>

#include <boost/filesystem.hpp>

#include "HpcServer/Server/Logger.hpp"
#include "means.hpp"

using namespace LogMudule;
using namespace std;


class Parser
{
public:
    Parser(const string &src, const string &dest)
        : _src_path(src),
          _dest_file_name(dest)
    {
        //去除干扰'/'
        if(_src_path.back()=='/')
        {
            _src_path.pop_back();
        }
    }
    // 递归式地枚举指定目录下的文件，把它的名字保存起来
    virtual bool EnumFileName() = 0;
    // 把去除文件中无效内容,然后把有效数据保存起来
    virtual bool ParserFile() = 0;
    // 把所有文件的有效数据全部写入到指定文件中，不同文件的有效数据之间以\3作为分隔符
    virtual bool Save() = 0;

    string SrcPath()
    {
        return _src_path;
    }
    string DestFileNeme()
    {
        return _dest_file_name;
    }
    void SetSrcPath(const string &path)
    {
        _src_path = path;
    }
    void SetDestFileNeme(const string &name)
    {
        _dest_file_name = name;
    }
    virtual ~Parser()
    {
    }

protected:
    string _src_path;       // 需要处理的文件所处的目录
    string _dest_file_name; // 处理之后，所有文件的有效数据所存放的文件
};

struct DocInfo
{
    string _title;   // 网页标题
    string _content; // 网页有效内容
    string _url;     // 网页URL
};

class HtmlParser : public Parser
{
    bool RecursionEnumFileName(const string &path)
    {
        // 使用boost库的filesystem命名空间
        namespace fs = boost::filesystem;
        // 定义path对象
        fs::path src_path(path);

        // 判断路径是否存在
        if (!fs::exists(src_path))
        {
            LOG(LogLevel::WARNING) << "需要处理的目录不存在!";
            return false;
        }
        // 使用迭代器遍历目录下的所有文件/目录
        for (const auto &entry : fs::directory_iterator(src_path))
        {
            // 如果不是普通文件
            if (!fs::is_regular_file(entry))
            {
                // 如果是目录，就进入递归，枚举该目录下的html文件
                if (RecursionEnumFileName(entry.path().string()) == false)
                    return false;
                continue;
            }
            // 如果文件后缀不是.html
            if (entry.path().extension() != ".html")
            {
                continue;
            }

            // 是普通文件并且文件后缀是.html，就保存它的路径
            _file_name_list.emplace_back(entry.path().string());
        }
        return true;
    }
    // 从html文件中提取出网页标题
    bool ParserTitle(const string &buf, string *title)
    {
        uint64_t start=0;
        uint64_t end=0;

        //查找标题起始标签位置
        if((start=buf.find("<title>"))==string::npos)
        {
            return false;
        }
         //查找标题结束标签位置
        if((end=buf.find("</title>",start))==string::npos)
        {
            return false;
        }

        if(start>end)
            return false;

        start+=sizeof("<title>")-1;
        
        //截取网页标题内容
        *title=buf.substr(start,end-start);

        return true; 
    }
    // 从html文件中提取出网页去除标签后的有效内容
    bool ParserContent(const string &buf, string *content)
    {
        enum status
        {
            LABEL,
            CONTENT
        };
        //正常html文件，一开始一定是标签
        enum status sta=LABEL;

        //标签一定是被<>包裹的
        for(auto ch:buf)
        {
            switch(sta)
            {
                case LABEL:
                    if(ch=='>')//一旦遇到>,就说明标签结束
                    {
                        sta=CONTENT;//进入网页正文
                    }
                    break;
                case CONTENT:
                    if(ch=='<')//一旦遇到<,就说明又进入了标签
                    {
                        sta=LABEL;//进入标签内部
                    }
                    else
                    {
                        //去除换行符
                        if(ch=='\n')
                            ch=' ';
                        content->push_back(ch);
                    }
                    break;
            }
        }
        return true;
    }
    // 从html文件中提取出网页url
    bool ParserUrl(const string &file_name,string *url)
    {
        string url_head = "https://www.boost.org/doc/libs/1_89_0/doc/html";

        string url_tail = file_name.substr(SrcPath().size());

        *url=url_head+url_tail;

        return true;
    }

public:
    HtmlParser(const string &src, const string &dest)
        : Parser(src, dest)
    {
    }
    // 递归式地枚举目录下的html文件，把它们的名字存放进_file_name_list中
    virtual bool EnumFileName()
    {
        if (RecursionEnumFileName(SrcPath()) == false)
            return false;
        else
            return true;
    }
    // 先去除标签，然后把去除标签后的所有文件的有效内容存放在_results中
    virtual bool ParserFile()
    {
        // 遍历解析所有html文件
        for (const auto &file_name : _file_name_list)
        {
            string buf;
            //读取文件中的所有内容
            if (means::ReadFile(file_name, &buf) == false)
                continue;

            DocInfo doc;

            //提取网页标题
            if(ParserTitle(buf,&doc._title)==false)
                continue;

            //提取出网页去除标签后的有效内容
            if(ParserContent(buf,&doc._content)==false)
                continue;

            //提取出网页url
            if(ParserUrl(file_name,&doc._url)==false)
                continue;

            //保存提取出来的内容
            _results.emplace_back(move(doc)); 
        }
        return true;
    }
    //_results中的数据全部写入到_dest_name中，不同文件的数据之间以\3作为分隔符
    virtual bool Save()
    {
        // 以追加写入打开文件
        ofstream ofs(DestFileNeme(),ios::app|ios::binary);
        // 如果打开失败
        if (ofs.is_open() == false)
        {
            LOG(LogLevel::WARNING) << DestFileNeme() << "打开失败";
            return false;
        }

        for(auto & doc:_results)
        {
            string data;
            data+=doc._title;
            data+=DocLineSep;
            data+=doc._content;
            data+=DocLineSep;
            data+=doc._url;
            data+='\n';

            //二进制方式写入
            ofs.write(data.c_str(),data.size());
        }

        ofs.close();
        return true;
    }

private:
    vector<string> _file_name_list; //_src_path目录下的，所有的html文件的名字
    vector<DocInfo> _results;       // 所有去除标签的html文件的有效文件内容
};
